package com.demo.jzoffer2;

import org.junit.Test;

/**
 * @author gy
 * @date 2023/03
 */
public class Test0316_01 {

    /**
     * 在一个 m*n 的棋盘的每一格都放有一个礼物，每个礼物都有一定的价值（价值大于 0）。
     * 你可以从棋盘的左上角开始拿格子里的礼物，并每次向右或者向下移动一格、直到到达棋盘的右下角。
     * 给定一个棋盘及其上面的礼物的价值，请计算你最多能拿到多少价值的礼物？
     *
     * 输入:
     * [
     *   [1,3,1],
     *   [1,5,1],
     *   [4,2,1]
     * ]
     * 输出: 12
     * 解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物
     *
     * 0 < grid.length <= 200
     * 0 < grid[0].length <= 200
     *
     * 思路分析：从棋盘的左上角开始拿各自里面的礼物，每次向右、向下移动一格、直到到棋盘的右下角；
     * 根据题目说明，易得 某单元格只可能从上边单元格或者左边单元格到达
     *
     * 设：f(i,j) 为从棋盘的左上角 到 单元格(i,j) 的礼物最大累计价值，易得到递推关系
     * f(i,j) 等于 f(i-1,j) 和 f(i,j-1) 的较大值 加上 当前单元格礼物价值 grid(i,j)
     * f(i,j) = max[f(i,j-1),f(i-1,j)] + grid(i,j)
     *
     * 动态规划解析：
     * 状态定义：
     * 设动态规划矩阵 dp，dp(i,j) 代表从棋盘的左上角 dp(0,0) 开始，到达单元格(i,j)时能拿到礼物的最大累计价值
     *
     * 转移方程：
     * 1.当 i = 0 且 j = 0 时，为起始元素；                       dp(i,j) = grid(i,j)
     * 2.当 i = 0 且 j ≠ 0 时，为矩阵第一行元素，只可以从左边到达    dp(i,j) = grid(i,j) + dp(i,j-1)
     * 3.当 i ≠ 0 且 j = 0 时，为矩阵第一列元素，只可以从上边到达    dp(i,j) = grid(i,j) + dp(i-1,j)
     * 4.当 i ≠ 0 且 j ≠ 0 时，可以从左边或者上边到达              dp(i,j) = grid(i,j) + max[dp(i,j-1),dp(i-1,j)]
     *
     * 初始状态：
     * dp[0][0] = grid[0][0] 即到达单元格 (0,0)时拿到的最大累计价值为 grid[0][0]
     *
     * 返回值：
     * dp[m-1][n-1],m,n 分别为矩阵的行高和列宽，即返回dp矩阵右下角元素
     *
     * 空间复杂度问题优化：
     * 由于 dp[i][j] 只有 dp[i-1][j] dp[i][j-1] grid[i][j]有关系，因此可以将原矩阵 grid用作dp矩阵，直接在 grid上修改即可
     * 应用此方法可以省去 dp矩阵使用的额外空间 O[MN] 降至 O[1]
     * 导致下面的代码 ，直接修改 原数据
     *
     * @param grid
     * @return
     */
    public int maxValue(int[][] grid) {
        // 空间复杂度问题优化：
        // 由于 dp[i][j] 只有 dp[i-1][j] dp[i][j-1] grid[i][j]有关系，
        // 因此可以将原矩阵 grid用作dp矩阵，直接在 grid上修改即可
        // 应用此方法可以省去 dp矩阵使用的额外空间 O[MN] 降至 O[1]
        // 导致下面的代码 ，直接修改 原数据
        int m = grid.length,n = grid[0].length;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (i==0 && j==0){
                    continue;
                }
                if (i == 0){
                    grid[i][j] += grid[i][j-1];
                }else if (j == 0){
                    grid[i][j] += grid[i-1][j];
                }else{
                    grid[i][j] += Math.max(grid[i][j-1],grid[i-1][j]);
                }
            }
        }
        return grid[m-1][n-1];
    }


    @Test
    public void m1() {
        int[][] ints = {{1,3,1},{1,5,1},{4,2,1}};
        this.maxValue(ints);
    }

    @Test
    public void m2() {

    }

    @Test
    public void m3(){

    }

}
